#ifndef MEMORYPOOL_H
#define MEMORYPOOL_H

#include <atomic>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#include <mutex>
#include <utility>

using std::mutex;
using std::atomic;
using std::forward;


namespace memorypool
{

// 共有64个池
#define MEMORY_POOL_NUMS 64 
// 第0个内存池中可分配最小字节数槽大小为8字节
#define BASIC_SLOT_SIZE 8
// 第63个内存池中可分配最大字节数槽大小为512字节
#define MAX_SLOT_SIZE 512

// 每个槽内存储一个原子的Slot*指针
struct Slot {
    std::atomic<Slot*> next;
};

// 内存池定义
class MemoryPool
{
public:
    /**
     * @brief 构造出一个内存池
     * 
     * @param BlockSize 块大小
     */
    MemoryPool(size_t BlockSize = 4096); // 这里必须加上默认参数，让其不传参数时为默认构造，否则内存池数组构建不出来
    ~MemoryPool();

    /**
     * @brief 初始化内存池
     * 
     * @param slotSize 该内存池中的Slot大小
     */
    void init(size_t slotSize);

    /**
     * @brief 从内存池中分配出一个Slot，返回该Slot的地址
     * 
     * @return void* 返回地址
     */
    void* allocate(); 
    /**
     * @brief 归还一个Slot到内存池中，参数为void*
     * 
     */
    void deallocate(void* ptr);
private:
    /**
     * @brief 向操作系统申请分配一个新的Block
     * 
     */
    void allocateNewBlock(); 
    /**
     * @brief 计算对齐到槽大小需要从分配的地址往后偏移的字节数
     * 
     * @param p 向操作系统分配来的块的起始地址
     * @param align SlotSize的值
     * @return size_t 需要从p向后移动多少个单位
     */
    size_t padPointer(char *p, size_t align);

    /**
     * @brief 将对象不使用的Slot归还到Block中
     * 
     * @param slot 待放大归还链表中的Slot型指针
     * @return true 放入成功
     * @return false 放入失败
     */
    bool pushSlotToFreeList(Slot* slot);

    /**
     * @brief 从已使用过但归还的Slot中取出一个来
     * 
     * @return Slot* 
     */
    Slot* popFreeListSlot();
private:
    size_t          _blockSize; // 内存池的块大小
    size_t          _slotSize; // 内存池块中每个Slot大小
    atomic<Slot*>   _freeListPtr; // 当前内存池中使用过但已被归还的第一个Slot的地址
    Slot*           _firstUnusedSlot; // 当前内存池中未被使用过的第一个Slot的地址
    Slot*           _firstBlock; // 当前内存池中第一个Block的首地址(当一个Block不够用时会再次申请)
    Slot*           _lastSlotPtr; // 当前内存池中最后一个Slot的首地址
    mutex           _forBlockMutex; // 保护Block的互斥量
};

class HashBucket
{
public:
    /**
     * @brief 初始化所有的内存池
     * 
     */
    static void initMemory();
    /**
     * @brief 获取某一个内存池对象
     * 
     * @param index 
     * @return MemoryPool& 
     */
    static MemoryPool& getMemory(int index);

    /**
     * @brief 使用内存池进行内存分配
     * 
     * @param size 需要分配的字节数
     * @return void* 返回的地址
     */
    static void* useMemory(size_t size);
    
    /**
     * @brief 释放从内存池中分配出来的内存
     * 
     * @param ptr 内存的起始地址
     */
    static void freeMemory(void* ptr, size_t size);

    /**
     * @brief 定义一个友元普通函数，为元素分配内存
     * 
     * @tparam T 
     * @tparam Args 
     * @return T* 
     */
    template<typename T, typename... Args>
    friend T* newElement(Args&&... args);

    template<typename T>
    friend void deleteElement(T* ptr);
};

template<typename T, typename... Args>
T* newElement(Args&&... args) {
    T* ptr = nullptr;
    if ((ptr = reinterpret_cast<T*>(HashBucket::useMemory(sizeof(T)))) != nullptr) {
        // 此时说明内存分配成功
        // 那么就使用placement_new来在分配的内存上构建对象
        // 使用forward进行完美转发，保持参数传递过程中的值类别
        new(ptr) T(forward<Args>(args)...);
    }
    return ptr;
}

template<typename T>
void deleteElement(T* ptr) {
    if (ptr) {
        // 先调用T的析构函数
        ptr->~T();
        // 然后调用freeMemory
        HashBucket::freeMemory(reinterpret_cast<void*>(ptr), sizeof(T));
    }
}

}
#endif
